﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Threading;
using net.entity.framework;
using net.entity.framework.db;

namespace net.entity.framework.cache {
    public class TypeIdCache {
        #region Members

        private static readonly ReaderWriterLockSlim syncLock = new ReaderWriterLockSlim();
        private static readonly Dictionary<int, EntityInfo> cache = new Dictionary<int, EntityInfo>();

        #endregion

        static TypeIdCache() {
            DataSet ds;
            using (var provider = new DataProvider(DbClient.SqlClient)) {
                ds = Repository.ExecuteQuery(provider, "SELECT * FROM TypeIdMapping");
            }
            foreach (DataRow row in ds.Tables[0].Rows) {
                var typeId = Convert.ToInt32(row[0]);
                var info = EntityInfo.Create(row[1].ToString());
                Add(typeId, info);
            }
        }

        #region Methods

        public static void Add(int typeId, EntityInfo entityInfo) {
            syncLock.EnterWriteLock();
            try {
                cache.Add(typeId, entityInfo);
            } finally {
                syncLock.ExitWriteLock();
            }
        }

        /// <summary>
        /// Gets the Entity type, given the typeId
        /// </summary>
        /// <param name="typeId">EqlEntity typeId</param>
        /// <returns></returns>
        public static EntityInfo GetValue(int typeId) {
            EntityInfo value;
            var found = false;
            syncLock.EnterReadLock();
            if (cache.TryGetValue(typeId, out value)) {
                found = true;
            }
            syncLock.ExitReadLock();
            if (found) {
                return value;
            }
            throw new KeyNotFoundException(string.Format("TypeIdCache: Cannot find key: '{0}'", typeId));
        }

        public static int GetTypeIdByEntityType(Type entityType) {
            foreach (var kvp in cache) {
                if (kvp.Value.EntityType == entityType) {
                    return kvp.Key;
                }
            }
            throw new KeyNotFoundException(string.Format("TypeIdCache: Cannot find TypeId for '{0}'",
                                                         entityType.FullName));
        }


        public static int GetTypeIdByEntityRefType(Type entityRefType) {
            foreach (var kvp in cache) {
                if (kvp.Value.EntityRefType == entityRefType) {
                    return kvp.Key;
                }
            }
            throw new KeyNotFoundException(string.Format("TypeIdCache: Cannot find TypeId for '{0}'",
                                                         entityRefType.FullName));
        }

        public static bool ContainsKey(int typeId) {
            syncLock.EnterReadLock();
            var contains = cache.ContainsKey(typeId);
            syncLock.ExitReadLock();
            return contains;
        }

        #endregion
    }

    public sealed class EntityInfo {
        public Type EntityRefType { get; set; }
        public Type EntityType { get; set; }

        public EntityInfo(Type entityRef, Type entity) {
            EntityRefType = entityRef;
            EntityType = entity;
        }

        public static EntityInfo Create(string entityTypeName) {
            var entityType = Type.GetType(entityTypeName, true);
            var entityRefType = Type.GetType(entityTypeName + "Ref", true);
            return new EntityInfo(entityRefType, entityType);
        }
    }
}